﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using System.Reflection;
using Microsoft.Xna.Framework;

namespace Innovation
{
    public class IEModel : Component
    {
        Model model;
        public Model ModelValue
        {
            get { return model; }
        }

        public Dictionary<ModelMeshPart, object> Materials = new Dictionary<ModelMeshPart, object>();

        public Actor ParentActor;

        string modelName;
        public string Model
        {
            get { return modelName; }
            set
            {
                modelName = value;
                model = Engine.Content.Load<Model>(value);
            }
        }

        public Matrix World = Matrix.Identity;

        public IEModel()
        {
            this.Visible = false;
        }

        public IEModel(Actor ParentActor)
        {
            if (ParentActor.Model != null)
                Model = ParentActor.Model;

            this.ParentActor = ParentActor;
            this.Visible = false;
        }

        public void GenerateMaterials<T>() where T : Material, new()
        {
            Dictionary<ModelMeshPart, T> materials = Material.SetupModel<T>(this);
            Dictionary<ModelMeshPart, object> convertedMaterials = new Dictionary<ModelMeshPart, object>();

            foreach (KeyValuePair<ModelMeshPart, T> pair in materials)
                convertedMaterials.Add(pair.Key, pair.Value);

            Materials = convertedMaterials;
        }

        public override void Draw()
        {
            Camera camera;

            try
            {
                // Look for a camera in the service container
                camera = Engine.Services.GetService<Camera>();
            }
            catch { return; }

            if (Model == null)
                return;

            // Get the base transforms of the model
            Matrix[] transforms = new Matrix[this.ModelValue.Bones.Count];
            this.ModelValue.CopyAbsoluteBoneTransformsTo(transforms);

            // Set some renderstates so the model will draw properly
            Engine.GraphicsDevice.RenderState.AlphaBlendEnable = true;
            Engine.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            Engine.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
            Engine.GraphicsDevice.RenderState.DepthBufferEnable = true;

            // Loop through meshes and effects and set them up to draw
            foreach (ModelMesh mesh in ModelValue.Meshes)
            {
                foreach(ModelMeshPart part in mesh.MeshParts)
                {
                    DefaultMaterial material = (DefaultMaterial)Materials[part];
                    material.World = transforms[mesh.ParentBone.Index] * World;
                    part.Effect = material.EffectValue;
                }

                // Draw the mesh
                mesh.Draw();
            }
        }

        public override void SaveSerializationData(SerializationData Data)
        {
            base.SaveSerializationData(Data);

            string mat = "";
            foreach (ModelMesh mesh in this.ModelValue.Meshes)
                foreach (ModelMeshPart part in mesh.MeshParts)
                    mat += ((Material)Materials[part]).Name + ",";

            Data.AddData("IEModel.ModelName", Model);
            Data.AddData("IEModel.Materials", mat);
            Data.AddData("IEModel.ParentActor", ParentActor.Name);
        }

        public override void LoadFromSerializationData(SerializationData Data)
        {
            base.LoadFromSerializationData(Data);

            this.Model = Data.GetData<string>("IEModel.ModelName");

            string[] materials = Data.GetData<string>("IEModel.Materials").Split(',');

            string parentName = Data.GetData<string>("IEModel.ParentActor");

            foreach(GameScreen screen in Engine.GameScreens)
                foreach(Component component in screen.Components)
                    if (component.Name == parentName)
                    {
                        this.ParentActor = (Actor)component;
                        ((Actor)component).ModelValue = this;
                    }

            int i = 0;

            foreach (ModelMesh mesh in ModelValue.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    foreach (GameScreen screen in Engine.GameScreens)
                        foreach (Component component in screen.Components)
                            if (component.Name == materials[i])
                            {
                                if (!Materials.ContainsKey(part))
                                    Materials.Add(part, component);
                                else
                                    Materials[part] = component;

                                ((Material)component).setMeshPart(this, part);
                            }
                    i++;
                }
            }

            lo = true;
        }

        bool lo = false;

        int MeshPartId(ModelMeshPart MeshPart)
        {
            string name = ((string[])MeshPart.Tag)[0];
            int id = name[name.Length - 1];

            return id;
        }
    }
}
